/*
 * monitor.c
 *
 *  Created on: Dec 13, 2011
 *      Author: piotrek
 */

#include "monitor.h"

const char *MMAPS_LIB_NUM_FILENAME = "maps_cnt";

#define MAX_BUFF_SIZE 8192

/*
 * Wrapper na informacje dotyczace kolejek
 */

void get_queue_info(int queue_id, struct msqid_ds *info) {
	int status = msgctl(queue_id, IPC_STAT, info);
	if(status == -1) {
		perror("Error while getting stats from the queue!");
	}
}


/*
 * Wrapper na funkcje pobierajaca identyfikator kolejki
 */
int get_queue_id(key_t key, int msgflag, pthread_t thread_id) {
	int queue_id = msgget(key, msgflag);

	if (queue_id == -1) {
		perror("Error while retrieving the queue!\n");
		log_thread_info("Error while retrieving the queue in thread %lu",
				thread_id);
	} else {
		log_thread_info("Queue id successfully retrieved! (Thread %lu)",
				thread_id);
		// wyswietlenie informacji na temat kolejki

		struct msqid_ds stats;
		get_queue_info(queue_id, &stats);
		LOG_MSG(" *** DISPLAYING QUEUE INFO ***");
		log_thread_info("Maximum number of bytes in queue: %lu", stats.msg_qbytes);
		log_thread_info("Current number of messages in queue: %lu", stats.msg_qnum);
	}

	return queue_id;
}

void align_left(char *str, int n)
{
	int i;
	for(i=strlen(str); i<n; i++){
		str[i] = ' ';
	}
	str[i] = '\0';
}

void append_horizontal_line(char *str, int n)
{
	int i;
	str += strlen(str);
	for(i=0; i<n; i++)
	{
		*str = '-';
		str += 1;
	}
	*str = '\n';
	str += 1;
	*str = '\0';
}

/*
 * Handler do oblsugi monitorowania procesu
 */
void * process_memory_monitoring_t_handler(void * data) {
	struct monitor_thread* mthread = (struct monitor_thread *) data;
	log_thread_info("Process monitoring thread (id = %lu) started", mthread->thread_id);

	int queue_id = get_queue_id(mthread->monitoring_req.message_queue_key, 0, mthread->thread_id);

	// jesli nie mamy kolejki - konczymy dzialanie
	if(queue_id == -1) {
		log_thread_info("Thread %lu exiting", mthread->thread_id);
		pthread_exit(NULL);
	}


	int msg_id = 1;

	while(1) {
		char *buff = malloc(MAX_BUFF_SIZE);

		size_t buff_pos = 0;
		fill_with_header(mthread, &buff, &buff_pos);
		fill_with_mmaps_lib_num(mthread, &buff, &buff_pos);
		fill_with_strace_mmaps(mthread, &buff, &buff_pos);
		fill_with_statm_info(mthread, &buff, &buff_pos);
		fill_with_stat_info(mthread, &buff, &buff_pos);

		LOG_MSG(buff);

		/*char *data = NULL;
		size_t data_size = create_fake_data(&data);*/

		send_data(queue_id, msg_id++, mthread->thread_id, buff, buff_pos+1);

		free(buff);
		sleep(mthread->monitoring_req.interval);
	}

	return NULL;
}

void append_fragmentation_info(char *ret)
{
	FILE *fd;
	char buf[10000];
	char *buff = buf;
	int n;
	char str[4][100];
	int ints[11];

	if( (fd = fopen("/proc/buddyinfo", "r")) == NULL )
	{
		LOG_MSG("Cannot open /proc/buddyinfo !");
		pthread_exit(NULL);
	}

	if( (n = fread(buf, 1, 10000, fd)) <= 0 )
	{
		LOG_MSG("Cannot read /proc/buddyinfo !");
		pthread_exit(NULL);
	}

	buf[n] = '\0';

	while( sscanf(buff, "%s%s%s%s%d%d%d%d%d%d%d%d%d%d%d", str[0], str[1], str[2], str[3],
			&ints[0], &ints[1], &ints[2], &ints[3], &ints[4], &ints[5], &ints[6], &ints[7], &ints[8], &ints[9], &ints[10]) > 0 )
	{
		int i;
		int prev_len = strlen(ret);
		strcat(ret, str[3]);
		align_left(ret, prev_len + 10);

		for(i=0; i<11; i++){
			char num[10];
			sprintf(num, "%8d", ints[i]);
			strcat(ret, num);
		}

		strcat(ret, "\n");
		append_horizontal_line(ret, 10 + 11*8);

		buff += 10;
		while(*buff != '\n' && *buff != '\0')
			buff += 1;

	}

	if(fclose(fd))
	{
		LOG_MSG("cannot close /proc/buddyinfo");
	}
}

void append_mem_usage_info(char *ret)
{
	FILE *fd;
	char buffer[10000];
	char *buff = buffer;
	char param[100];
	unsigned long value;
	int memTotal = 0;
	int memFree = 0;
	unsigned long vMallocTotal = 0;
	unsigned long vMallocUsed = 0;
	int n;

	fd = fopen( "/proc/meminfo", "r" );

	if(fd == NULL)
	{
		LOG_MSG("Cannot open /proc/meminfo");
		pthread_exit(NULL);
	}

	if( (n = fread(buffer, 1, 10000, fd )) <= 0 )
	{
		LOG_MSG("Cannot read /proc/meminfo");
		pthread_exit(NULL);
	}

	buffer[n] = '\n';

	while( sscanf( buff, "%s%lu", param, &value ) > 0 )
	{

		if( !strcmp( param, "MemTotal:" ) )
		{
			memTotal = value;
		}

		if( !strcmp( param, "MemFree:" ) )
		{
			memFree = value;
		}

		if( !strcmp( param, "VmallocTotal:" ))
		{
			vMallocTotal = value;
		}

		if( !strcmp( param, "VmallocUsed:" ))
		{
			vMallocUsed = value;
		}

		buff += 1;

		while( *buff != '\0' && *buff != '\n')
			buff += 1;
	}

	strcat(ret, "MemTotal   MemFree    vMallocTot  vMallocUsed\n");
	strcat(ret, "---------------------------------------------\n");
	sprintf(param, "%-10d %-10d %-10lu %-10lu\n", memTotal, memFree, vMallocTotal, vMallocUsed);
	strcat(ret, param);
	if(fclose(fd))
	{
		LOG_MSG("cannot close /proc/meminfo");
	}
}

int is_number(const char* str)
{
	int i;

	for( i=0; i<strlen(str); i++ )
	{
		if( !isdigit(str[i]) )
			return 0;
	}

	return 1;
}

void append_processes_info(char *ret)
{
	DIR *dir = opendir("/proc");
	struct dirent *dirent;
	unsigned long sum_minflt = 0;
	unsigned long sum_majflt = 0;
	char buf[400];

	if(dir == NULL)
	{
		LOG_MSG("Cannot open a dir /proc");
		pthread_exit(NULL);
	}

	while( (dirent = readdir(dir)) != NULL )
	{

		if( dirent->d_type != DT_DIR )
			continue;

		if( is_number(dirent->d_name) )
		{
			int fake_i;
			unsigned long fake_ul, minflt, cminflt, majflt, cmajflt;
			char fake_buf[100];
			char fake_c;
			char path[100];
			int n;

			FILE *fd;

			strcpy(path, "/proc/");
			strcat(path, dirent->d_name);
			strcat(path, "/statm");

			fd = fopen(path, "r");

			if(fd == NULL)
				continue;

			n = fscanf(fd, "%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu",
						&fake_i /* pid */,
						fake_buf /* comm */,
						&fake_c /* state */,
						&fake_i /* ppid */,
						&fake_i /* pgrp */,
						&fake_i /* session */,
						&fake_i /* terminal */,
						&fake_i /* tpgid */,
						&fake_ul /*flags*/,
						&minflt,
						&cminflt,
						&majflt,
						&cmajflt
						);

			sum_minflt += minflt;
			sum_majflt += majflt;

			fclose(fd);

		}
	}

	sprintf(buf, "Summary of the minor page faults: %lu\nSummary of the major page faults: %lu\n", sum_minflt, sum_majflt);
	strcat(ret, buf);

	closedir(dir);
}

/*
 * Handler do obslugi monitorowania systemu
 */
void *system_memory_monitoring_t_handler(void * data) {

	struct monitor_thread* mthread = (struct monitor_thread *) data;

	char ret[10000];

	int msg_id = 1;

	log_thread_info("System monitoring thread (id = %lu) started", mthread->thread_id);

	int queue_id = get_queue_id(mthread->monitoring_req.message_queue_key, 0, mthread->thread_id);

	if(queue_id == -1)
	{
		return NULL;
	}

	while(1)
	{

		ret[0] = '\0';

		append_fragmentation_info( ret );
		append_mem_usage_info( ret );
		append_processes_info( ret );

		send_data(queue_id, msg_id++, mthread->thread_id, ret, strlen(ret)+1);

		sleep(mthread->monitoring_req.interval);
	}

	return NULL;
}

